
/* <<< COPYRIGHT START >>>
 * Copyright 2006-Present OxygenSoftwareLibrary.com
 * Licensed under the GNU Lesser General Public License.
 * http://www.gnu.org/licenses/lgpl.html
 * 
 * @author: Ugorji Dick-Nwoke
 * <<< COPYRIGHT END >>>
 */


package oxygen.tool.classcompatibilityinspector;

import java.io.IOException;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;

/** Adapted from code given to Ugorji by Anno Langen */
public class CCIClassMarshaller implements Serializable {
  String clazz;
  String type;
  String name;

  public CCIClassMarshaller(String c, String n, String t) {
    clazz = c;
    name = n;
    type = t;
  }
    
  public boolean equals(Object other) {
    CCIClassMarshaller o = (CCIClassMarshaller) other;
    return clazz.equals(o.clazz) && 
      name.equals(o.name) &&
      (type == null? o.type == null: type.equals(o.type));
  }

  public int hashCode() {
    return clazz.hashCode() ^ type.hashCode() ^ name.hashCode();
  }

  public String asUse() {
    try {
      return writeUse("m", new StringWriter()).toString();
    } catch (IOException ioe) {
      throw new AssertionError(ioe);
    }
  }

  public Writer writeUse(String mName, Writer out) throws IOException {
    // rType mName(clazz o) throws Throwable {return o.name(paramTypes);}
    int e = type.lastIndexOf(')');
    boolean isField = e == -1;
    if (isField) out.write("void");
    else write1Type(out, e+1);

    out.write(' ');
    out.write(mName);
    out.write('(');
    out.write(in2ex(clazz));
    out.write(" o) throws Throwable { ");
    if (isField) {
      // fields need need to be wrapped with a statement
      out.write("System.out.print(");
    } else if (type.charAt(e+1) != 'V') {
      out.write("return ");
    }
    out.write("o.");
    out.write(name);
    if (isField) {
      out.write(");}");
      return out;
    } 
    out.write('(');
    String sep = "";
    for (int i = 1; i < e; i = write1Use(out, i)) {
      out.write(sep);
      sep = ", ";
    }
    out.write(");}");
    return out;
  }

  public String asApiDoc() {
    try {
      return writeApiDoc(new StringWriter()).toString();
    } catch (IOException ioe) {
      throw new AssertionError(ioe);
    }
  }

  public Writer writeApiDoc(Writer out) throws IOException {
    out.write(in2ex(clazz));
    out.write('.');
    int si = name.lastIndexOf('/');
    if (si == -1) {
      out.write(name);
    } else {
      int di = name.lastIndexOf('$');
      out.write(name.substring(1+(si < di? di: si)));
    }
    if (type.charAt(0) != '(') return out;
    out.write('(');
    String sep = "";
    int e = type.lastIndexOf(')');
    for (int i = 1; i < e; i = write1Type(out, i)) {
      out.write(sep);
      sep = ", ";
    }
    out.write(')');
    return out; 
  }

  private static String in2ex(String s) {
    return s.replace('/', '.').replace('$', '.');
  }

  private int write1Type(Writer out, int i) throws IOException {
    int dimension = 0;
    char c = type.charAt(i);
    for (; c == '['; c = type.charAt(++i)) dimension++;
    if (c == 'L') {
      int e = type.indexOf(';', i);
      out.write(in2ex(type.substring(i+1, e)));
      while (dimension-- > 0) out.write("[]");
      return e+1;
    }
    out.write(sourceType(c));
    while (dimension-- > 0) out.write("[]");
    return i+1;
  }

  private int write1Use(Writer out, int i) throws IOException {
    out.write('(');
    int j = write1Type(out, i);
    out.write(") ");
    out.write(typeValue(type.charAt(i)));
    return j;
  }

  private static String sourceType(char c) {
    switch (c) {
    case 'B': return "byte";
    case 'C': return "char";
    case 'D': return "double";
    case 'F': return "float";
    case 'I': return "int";
    case 'J': return "long";
    case 'S': return "short";
    case 'V': return "void";
    case 'Z': return "boolean";
    }
    throw new AssertionError("c="+c);
  }

  private static String typeValue(char c) {
    switch (c) {
    case '[':
    case 'L':
      return "null";
    case 'Z':
      return "false";
    default:
      return "0";
    }
  }
  
}
